/*
============================================================================
 Name        : nmsJpegCompress.c
 Author      : Jian
 Version     :
 Copyright   : base on http://ce.sharif.edu/~ahmadinejad/jpeg/
 Description : nmsJpegCompress.c - source file
============================================================================
*/

/* INCLUDE FILES */
#include "nmsJpegCompress.h"
#include "nmsHuffman.h"
#include "nmsFrame.h"
#include "nmsJpeg.h"
#include "nmsJpegUtil.h"
#include "nmsJpegMcuReader.h"

#define __COMPSOR_MAX_MCU  16
#define __COMPSOR_BUF_LEN  512
#define __MCU_WIDTH        16
#define __MCU_HEIGHT       16

#define __abs(c) ((c) >= 0 ? (c) : -(c))

#ifdef __NMS_JPEG_GOOD_QUALITY

static const UINT8 _yQtTable[64] = {
    8,  6,  5,  8,  12, 20, 26, 31,
    6,  6,  7,  10, 13, 29, 30, 28,
    7,  7,  8,  12, 20, 29, 35, 28,
    7,  9,  11, 15, 26, 44, 40, 31,
    9,  11, 19, 28, 34, 55, 52, 39,
    12, 18, 28, 32, 41, 52, 57, 46,
    25, 32, 39, 44, 52, 61, 60, 51,
    36, 46, 48, 49, 56, 50, 52, 50
};

static const UINT8 _cbcrQtTable[64] = {
    9,  9,  12, 24, 50, 50, 50, 50,
    9,  11, 13, 33, 50, 50, 50, 50,
    12, 13, 28, 50, 50, 50, 50, 50,
    24, 33, 50, 50, 50, 50, 50, 50,
    50, 50, 50, 50, 50, 50, 50, 50,
    50, 50, 50, 50, 50, 50, 50, 50,
    50, 50, 50, 50, 50, 50, 50, 50,
    50, 50, 50, 50, 50, 50, 50, 50
};

#else

static const UINT8 _yQtTable[64] = {
    16, 11, 10, 16, 24, 40, 51, 61,
    12, 12, 14, 19, 26, 58, 60, 55,
    14, 13, 16, 24, 40, 57, 69, 56,
    14, 17, 22, 29, 51, 87, 80, 62,
    18, 22, 37, 56, 68,109,103, 77,
    24, 35, 55, 64, 81,104,113, 92,
    49, 64, 78, 87,103,121,120,101,
    72, 92, 95, 98,112,100,103, 99
} ;

static const UINT8 _cbcrQtTable[64] = {
    17, 18, 24, 47, 99, 99, 99, 99,
    18, 21, 26, 66, 99, 99, 99, 99,
    24, 26, 56, 99, 99, 99, 99, 99,
    47, 66, 99, 99, 99, 99, 99, 99,
    99, 99, 99, 99, 99, 99, 99, 99,
    99, 99, 99, 99, 99, 99, 99, 99,
    99, 99, 99, 99, 99, 99, 99, 99,
    99, 99, 99, 99, 99, 99, 99, 99
} ;

#endif

static const UINT8 _yDCHTableCodeCount[17]    = { 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 };
static const UINT8 _yDCHTableValues[12]       = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };

static const UINT8 _yACHTableCodeCount[17]    = { 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d };
static const UINT8 _yACHTableValues[162]      = {
    0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32,
    0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
    0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45,
    0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
    0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94,
    0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
    0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8,
    0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
    0xf9, 0xfa
};

static const UINT8 _cbcrDCHTableCodeCount[17] = { 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 };
static const UINT8 _cbcrDCHTableValues[12]    = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };

static const UINT8 _cbcrACHTableCodeCount[17] = { 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 };
static const UINT8 _cbcrACHTableValues[162]   = {
    0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81,
    0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
    0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44,
    0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
    0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92,
    0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
    0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6,
    0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
    0xf9, 0xfa
};


extern const UINT8 _zigZagScanner[64] ;
extern nmsRgbMatrix nmsRgbCache ;

typedef struct __nms_jpeg_compressor {

    nmsJpeg*           jpg ;
    HFILE              dumpFile ;
    int                scale ;

    nmsJpegMcu*        lineReader[__COMPSOR_MAX_MCU] ;
    int                readerCount ;

    UINT8              cacheBuf[__COMPSOR_BUF_LEN + 6] ;
    int                bytePos ;
    int                bitPos ;

    const UINT8*       qt[__NMC_JPEG_COMP_ID_CR+1] ;
    nmsHuffmanBuilder* huffman[__NMC_JPEG_COMP_ID_CR+1][2] ;    /* index 0 is DC table, 1 is AC table */

    int                preDC[__NMC_JPEG_COMP_ID_CR + 1] ;

    int                rgbArray[2][256] ;
    int                workIndex ;
    int                nextX ;

    nmsHufmmanVal      ac0Val[__NMC_JPEG_COMP_ID_CR+1] ;
    nmsHufmmanVal      acF0Val[__NMC_JPEG_COMP_ID_CR+1] ;

} nmsJpegCompressor;

static void _initHVal(nmsHufmmanVal* val, UINT16 code, UINT8 bitLen, UINT8 posInHt)
{
    val->code    = code ;
    val->bitLen  = bitLen ;
#ifdef __NMS_JPEG_DEBUG
    val->posInHt = posInHt ;
#endif
}

static __NMS_INLINE nmsJpegCompressor* _createCompressor(struct __nms_jpeg* jpg, HFILE dumpFile, int scale)
{
    nmsJpegCompressor* compsor = (nmsJpegCompressor*)nmsJpegMalloc(sizeof(nmsJpegCompressor)) ;
    nmsMemSet(compsor, 0, sizeof(nmsJpegCompressor)) ;
    compsor->jpg      = jpg ;
    compsor->dumpFile = dumpFile ;
    compsor->scale    = scale ;

    compsor->qt[__NMC_JPEG_COMP_ID_Y]          = _yQtTable ;
    compsor->qt[__NMC_JPEG_COMP_ID_CB]         = _cbcrQtTable ;
    compsor->qt[__NMC_JPEG_COMP_ID_CR]         = _cbcrQtTable ;

    compsor->huffman[__NMC_JPEG_COMP_ID_Y][0]  = nmsHuffmanBuild(_yDCHTableCodeCount, _yDCHTableValues, 11) ;
    compsor->huffman[__NMC_JPEG_COMP_ID_Y][1]  = nmsHuffmanBuild(_yACHTableCodeCount, _yACHTableValues, 0xfa) ;

    compsor->huffman[__NMC_JPEG_COMP_ID_CB][0] = nmsHuffmanBuild(_cbcrDCHTableCodeCount, _cbcrDCHTableValues, 11) ;
    compsor->huffman[__NMC_JPEG_COMP_ID_CB][1] = nmsHuffmanBuild(_cbcrACHTableCodeCount, _cbcrACHTableValues, 0xfa) ;

    compsor->huffman[__NMC_JPEG_COMP_ID_CR][0] = compsor->huffman[__NMC_JPEG_COMP_ID_CB][0] ;
    compsor->huffman[__NMC_JPEG_COMP_ID_CR][1] = compsor->huffman[__NMC_JPEG_COMP_ID_CB][1] ;

    _initHVal(&(compsor->ac0Val[__NMC_JPEG_COMP_ID_Y]), 10, 4, 3) ;
    _initHVal(&(compsor->ac0Val[__NMC_JPEG_COMP_ID_CB]), 0, 2, 0) ;
    _initHVal(&(compsor->ac0Val[__NMC_JPEG_COMP_ID_CR]), 0, 2, 0) ;

    _initHVal(&(compsor->acF0Val[__NMC_JPEG_COMP_ID_Y]), 2041, 11, 31) ;
    _initHVal(&(compsor->acF0Val[__NMC_JPEG_COMP_ID_CB]), 1018, 10, 31) ;
    _initHVal(&(compsor->acF0Val[__NMC_JPEG_COMP_ID_CR]), 1018, 10, 31) ;

    return compsor ;
}

static __NMS_INLINE void _destroyCompressor(nmsJpegCompressor* compsor)
{
    if (compsor) {
        int i = 0 ;
        for (; i < __COMPSOR_MAX_MCU; i++) {
            if (compsor->lineReader[i])
                nmsMcuDesctroy(compsor->lineReader[i]) ;
        }

        nmsHuffmanBuilderFree(compsor->huffman[__NMC_JPEG_COMP_ID_Y][0]) ;
        nmsHuffmanBuilderFree(compsor->huffman[__NMC_JPEG_COMP_ID_Y][1]) ;
        nmsHuffmanBuilderFree(compsor->huffman[__NMC_JPEG_COMP_ID_CB][0]) ;
        nmsHuffmanBuilderFree(compsor->huffman[__NMC_JPEG_COMP_ID_CB][1]) ;

        nmsJpegFree(compsor) ;
    }
}

static void _dumpDHT(HFILE dumpHandle, int isAc, int index, const UINT8* htCount, const UINT8* htVals, int htValCount)
{
    int byteWritten   = 0 ;

    UINT8 htHeader[5] = { 0xFF, 0xC4, 0, 0, 0 } ;
    UINT16 len        = 2 + 1 + 16 + htValCount ;

    htHeader[2] = (UINT8)(len >> 8) ;
    htHeader[3] = (UINT8)len ;
    htHeader[4] = ((UINT8)(isAc ? 0x10 : 0)) | ((UINT8)index) ;

    nmsFsWrite(dumpHandle, htHeader, 5, &byteWritten) ;

    nmsFsWrite(dumpHandle, (void*)(htCount + 1), 16, &byteWritten) ;

    nmsFsWrite(dumpHandle, (void*)htVals, htValCount, &byteWritten) ;
}

static __NMS_INLINE void _dumpHeader(struct __nms_jpeg* jpg, HFILE dumpHandle, UINT32 scale)
{
    int byteWritten = 0 ;

    /* __SEP and __APP0 maker : start of image and JEIF v1.2 */
    UINT8 startMaker[20] = {
        0xFF, 0xD8, 0xFF, 0xE0, 0, 0x10, 0x4A, 0x46, 0x49, 0x46,
        0, 0x1, 0x2, 0, 0, 0x1, 0, 0x1, 0, 0
    } ;

    nmsFsWrite(dumpHandle, startMaker, 20, &byteWritten) ;

    {
        /* __DQT 2 qt 0 is yQt, 1 is cbcrQt */
        UINT8 zigZag[64] = {0} ;
        UINT8 qtHeader[5] = {0xFF, 0xDB, 0, 0x43, 0} ;
        nmsFsWrite(dumpHandle, qtHeader, 5, &byteWritten) ;
        memcpy(zigZag, _yQtTable, 64) ;
        nmsJpegToZiZag1(zigZag) ;
        nmsFsWrite(dumpHandle, (void*)zigZag, 64, &byteWritten) ;
        qtHeader[4] = 1 ;
        memcpy(zigZag, _cbcrQtTable, 64) ;
        nmsJpegToZiZag1(zigZag) ;
        nmsFsWrite(dumpHandle, qtHeader, 5, &byteWritten) ;
        nmsFsWrite(dumpHandle, (void*)zigZag, 64, &byteWritten) ;
    }

    {
        /* __SOF0 start of frame for Yu411 */
        UINT8 sofMaker[19] = {
            0xFF, 0xC0, 0, 0x11, 0x8, 0, 0, 0, 0,
            0x3, 0x1, 0x22, 0, 0x2, 0x11, 0x1, 0x3, 0x11, 0x1
        } ;

        UINT16 compWidth  = jpg->frame->width * scale / 100 ;
        UINT16 compHeight = jpg->frame->height * scale / 100 ;

        sofMaker[5] = (UINT8)(compHeight >> 8) ;
        sofMaker[6] = (UINT8)compHeight ;
        sofMaker[7] = (UINT8)(compWidth  >> 8) ;
        sofMaker[8] = (UINT8)compWidth ;

        nmsFsWrite(dumpHandle, sofMaker, 19, &byteWritten) ;
    }

    {
        /* __DHT 4 */
        _dumpDHT(dumpHandle, 0, 0, _yDCHTableCodeCount, _yDCHTableValues, sizeof(_yDCHTableValues)) ;
        _dumpDHT(dumpHandle, 1, 0, _yACHTableCodeCount, _yACHTableValues, sizeof(_yACHTableValues)) ;

        _dumpDHT(dumpHandle, 0, 1, _cbcrDCHTableCodeCount, _cbcrDCHTableValues, sizeof(_cbcrDCHTableValues)) ;
        _dumpDHT(dumpHandle, 1, 1, _cbcrACHTableCodeCount, _cbcrACHTableValues, sizeof(_cbcrACHTableValues)) ;
    }

    {
        /* __SOS */
        UINT8 sosMaker[14] = { 0xFF, 0xDA, 0x00, 0x0C,
                               0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3F, 0x00
                             } ;

        nmsFsWrite(dumpHandle, sosMaker, 14, &byteWritten) ;
    }
}

static nmsHufmmanVal _toDcAcVal(int val)
{
    nmsHufmmanVal ret = {0};
    int absVal        = 0 ;
    int bitLen        = 10 ;
    int codeStart     = 0 ;

    if (val == 0)  return ret ;

    absVal = __abs(val) ;

    __NMS_ASSERT_DEFENSE(absVal <= 2047, return ret) ;

    for (; bitLen >= 0; bitLen--) {
        if (absVal & (1 << bitLen))
            break ;
    }
    bitLen++ ;

    codeStart = (1 << (bitLen - 1)) ;

    if (val >= 0)
        ret.code = codeStart + (absVal - codeStart);
    else
        ret.code = codeStart - (absVal - codeStart + 1);

    ret.bitLen = bitLen ;

    return ret ;
}

static void _dumpHuffmanValToBuf(nmsJpegCompressor* compsor, nmsHufmmanVal val)
{
    int i = val.bitLen - 1;
    for (; i >= 0; i--) {
        compsor->cacheBuf[compsor->bytePos] |= (UINT8)((((val.code & (1 << i)) >> i) << (8 - 1 - compsor->bitPos))) ;
        compsor->bitPos++ ;
        if (compsor->bitPos == 8) {
            compsor->bitPos = 0 ;
            compsor->bytePos += ((compsor->cacheBuf[compsor->bytePos] != 0xFF) ? 1 : 2) ; /* Add 0xFF 0x00 for 0xFF */
        }
    }

    if (compsor->bytePos >= __COMPSOR_BUF_LEN) {
        int dummy     = 0 ;
        int saveByte  = compsor->cacheBuf[compsor->bytePos] ;
        nmsFsWrite(compsor->dumpFile, compsor->cacheBuf, compsor->bytePos, &dummy) ;
        nmsMemSet(compsor->cacheBuf, 0, sizeof(compsor->cacheBuf)) ;
        compsor->bytePos     = 0 ;
        compsor->cacheBuf[0] = saveByte ;
    }
}

static __NMS_INLINE void _dumpRemainBuf(nmsJpegCompressor* compsor)
{
    int dumpCount = 0 ;
    int dummy     = 0 ;
    if (compsor->bytePos == 0 && compsor->bitPos == 0)
        return ;

    dumpCount = compsor->bytePos ;

    if (compsor->bitPos != 0) {
        compsor->cacheBuf[compsor->bytePos] |= (UINT8)(0xFF >> compsor->bitPos) ;  /* Replace all remain bit with 1 */
        dumpCount++ ;
    }

    nmsFsWrite(compsor->dumpFile, compsor->cacheBuf, dumpCount, &dummy) ;
}


static int _dumpDu(nmsJpegCompressor* compsor, int compId, int* duData)
{
    const UINT8* qt  = NULL;
    int tmpData[64]  = {0} ;
    int i            = 0 ;
    int zigIndex     = 0 ;

    /* IDT */
    nmsJpegDct(duData, tmpData) ;

    /* Quantization and Zig-Zag */
    qt = compsor->qt[compId] ;
    for (i = 0; i < 64; i++) {
        zigIndex  = _zigZagScanner[i] ;
        duData[i] = (tmpData[zigIndex] / (int)(qt[zigIndex])) ;
    }

    /* DC */
    {
        nmsHuffmanBuilder* dcHt = compsor->huffman[compId][0] ;
        int diff                = duData[0] - compsor->preDC[compId] ;
        nmsHufmmanVal val       =  _toDcAcVal(diff) ;
        nmsHufmmanVal lenVal    = nmsHuffmanGet(dcHt, val.bitLen) ;

        compsor->preDC[compId]  = duData[0] ;

        _dumpHuffmanValToBuf(compsor, lenVal) ;
        _dumpHuffmanValToBuf(compsor, val) ;
    }

    /* AC */
    {
        nmsHuffmanBuilder* acHt = compsor->huffman[compId][1] ;
        int   zeroCount         = 0 ;
        int   zeroF0Count       = 0 ;
        UINT8 tmpVal            = 0 ;
        nmsHufmmanVal val       = {0} ;
        nmsHufmmanVal rlVal     = {0} ;

        for (i = 1; i < 64; i++) {
            /* run length */
            if (duData[i] == 0) {
                zeroCount ++ ;
                if (zeroCount == 16) {
                    zeroF0Count++ ;
                    zeroCount = 0 ;
                }
            } else {
                int j = 0 ;
                for (; j < zeroF0Count; j++)
                    _dumpHuffmanValToBuf(compsor, compsor->acF0Val[compId]) ;
                zeroF0Count = 0 ;

                val   = _toDcAcVal(duData[i]) ;
                rlVal = nmsHuffmanGet(acHt, (((UINT8)zeroCount) << 4) | (((UINT8)val.bitLen) & 0xF)) ;
                _dumpHuffmanValToBuf(compsor, rlVal) ;
                _dumpHuffmanValToBuf(compsor, val) ;
                zeroCount = 0 ;
            }
        }

        /* EOB */
        if (zeroCount != 0 || zeroF0Count != 0)
            _dumpHuffmanValToBuf(compsor, compsor->ac0Val[compId]) ;

    }

    return 0 ;
}

/*
* Y  = 0.299*R + 0.587*G + 0.114*B
* Cb = - 0.1687*R - 0.3313*G + 0.5 *B + 128
* Cr = 0.5 *R - 0.4187*G - 0.0813*B + 128
*/

static __NMS_INLINE void _getYDuData(int* rgbArray, int yIndex, int* duData)
{
    int rgb    = 0 ;
    int* start = rgbArray ;
    int i, j ;

    if (yIndex == 1)      start += 8 ;
    else if (yIndex == 2) start += __MCU_WIDTH * 8 ;
    else if (yIndex == 3) start += __MCU_WIDTH * 8 + 8 ;

    for (i = 0; i < 8; i++) {
        for (j = 0; j < 8; j++) {
            rgb = (*start++) ;
            duData[i * 8 + j] = (INT8)((299 * (__getR(rgb) - 128) + 587 * (__getG(rgb) - 128) + 114 * (__getB(rgb) - 128) + 500) / 1000) ; ;
        }
        start += 8 ;
    }
}


static __NMS_INLINE void _geCbCrDuData(int* rgbArray,int* duData, int* factor)
{
    int rgb1, rgb2, rgb3, rgb4;
    INT8 r, g, b ;
    int i, j, index;


    for (i = 0; i < 8; i++) {
        for (j = 0; j < 8; j++) {
            index = j * 2 + i * 2 * __MCU_WIDTH ;
            rgb1 = rgbArray[index] ;
            rgb2 = rgbArray[index+ 1] ;
            rgb3 = rgbArray[index + __MCU_WIDTH] ;
            rgb4 = rgbArray[index + __MCU_WIDTH + 1] ;

            r = (UINT32)(__getR(rgb1) + __getR(rgb2) + __getR(rgb3) + __getR(rgb4)) / 4 - 128;
            g = (UINT32)(__getG(rgb1) + __getG(rgb2) + __getG(rgb3) + __getG(rgb4)) / 4 - 128;
            b = (UINT32)(__getB(rgb1) + __getB(rgb2) + __getB(rgb3) + __getB(rgb4)) / 4 - 128;

            /* r = __getR(rgb1) - 128; g =  __getG(rgb1) - 128; b = __getB(rgb1) - 128; */

            duData[i * 8 + j] = (INT8)((factor[0] * r + factor[1] * g + factor[2] * b + 5000) / 10000);
        }
    }
}


static __NMS_INLINE void _dumpMCU(nmsJpegCompressor* compsor, int* rgbArray)
{
    int duData[64]  = {0} ;
    int cbFactor[3] = {-1687, -3313, 5000} ;
    int crFactor[3] = {5000, -4187, -813} ;

    int i = 0 ;
    for (; i < 4; i++) {
        _getYDuData(rgbArray, i, duData) ;
        _dumpDu(compsor, __NMC_JPEG_COMP_ID_Y, duData) ;
    }

    _geCbCrDuData(rgbArray, duData, cbFactor) ;
    _dumpDu(compsor, __NMC_JPEG_COMP_ID_CB, duData) ;

    _geCbCrDuData(rgbArray, duData, crFactor) ;
    _dumpDu(compsor, __NMC_JPEG_COMP_ID_CR, duData) ;
}

static int _initLineReaders(nmsJpegCompressor* compsor, int lineNo)
{
    int preDC[__NMC_JPEG_COMP_ID_CR + 1]  = {0} ;
    int nextDC[__NMC_JPEG_COMP_ID_CR + 1] = {0} ;
    int startPos      = compsor->jpg->imgDataPos * 8 ;
    int compY         =  lineNo * __MCU_HEIGHT;
    int startReadLine = 0 ;
    int endReadLineNo = 0 ;
    int readLines[32] = {0} ;
    int i             = 0;

    if (lineNo > 0) {
        __NMS_ASSERT_DEFENSE(compsor->readerCount > 0, return -1) ;
        if (nmsMcuCmpYPixel(compsor->lineReader[compsor->readerCount - 1], compY * 100 / compsor->scale) == 0) {
            __NMS_ASSERT_DEFENSE(compsor->readerCount > 1, return -1) ;
            startPos = compsor->lineReader[compsor->readerCount - 1]->lineStartBitPos ;
            nmsMemCpy(preDC, compsor->lineReader[compsor->readerCount - 2]->preDC, sizeof(preDC)) ;
        } else {
            startPos = nmsMcuGetNextLineStartPos(compsor->lineReader[compsor->readerCount - 1]) ;
            nmsMemCpy(preDC, compsor->lineReader[compsor->readerCount - 1]->preDC, sizeof(preDC)) ;
        }
    }

    startReadLine = ((lineNo * __MCU_HEIGHT) * 100 / compsor->scale) / compsor->jpg->mcuHeight ;

    if (startReadLine >= compsor->jpg->mcuLine) {
        startReadLine = compsor->jpg->mcuLine - 1 ;
        endReadLineNo = startReadLine ;
    } else {
        for (i = 0; i < 16; i++) {
            int tmp = ((lineNo * __MCU_HEIGHT + i) * 100 / compsor->scale) / compsor->jpg->mcuHeight ;
            if (tmp < compsor->jpg->mcuLine) {
                readLines[tmp - startReadLine] = 1 ;
                if (endReadLineNo < tmp)  endReadLineNo = tmp ;
            }
        }
    }

    __NMS_ASSERT_DEFENSE(startReadLine <= endReadLineNo, return -1) ;

    i = startReadLine ;
    compsor->readerCount = 0 ;
    while(i < endReadLineNo) {
        if (!compsor->lineReader[compsor->readerCount])
            compsor->lineReader[compsor->readerCount] = nmsMcuCreate(compsor->jpg) ;
        startPos = nmsMcuInit(compsor->lineReader[compsor->readerCount], startPos, i, preDC, nextDC) ;
        nmsMemCpy(preDC, nextDC, sizeof(preDC)) ;
        if (readLines[i - startReadLine]) {
            compsor->readerCount++ ;
            __NMS_ASSERT_DEFENSE(compsor->readerCount < __COMPSOR_MAX_MCU, return -1) ;
        }
        i++ ;
    }

    if (!compsor->lineReader[compsor->readerCount])
        compsor->lineReader[compsor->readerCount] = nmsMcuCreate(compsor->jpg) ;

    nmsMcuInit(compsor->lineReader[compsor->readerCount], startPos, endReadLineNo, preDC, NULL) ;
    compsor->readerCount++ ;

    return 0 ;
}


static __NMS_INLINE int _readCompMcu(nmsJpegCompressor* compsor, int xLineNo, int yLineNo)
{
    int x, y, i, k;
    int lineStartX  = 0 ;
    int* workRgbA   = compsor->rgbArray[compsor->workIndex] ;
    int* nextRgbA   = compsor->rgbArray[(compsor->workIndex == 0) ? 1 : 0] ;
    int workX[16]   = {0} ;
    int workY[16]   = {0} ;
    int nextX[16]   = {0} ;
    int startY      = 0 ;
    int endY        = 0 ;
    nmsJpegMcu* mcu = NULL ;
    int nextXStart  = 0 ;

    for (i = 0; i < 16; i++) {
        workX[i] = (xLineNo * __MCU_WIDTH + i) * 100 / compsor->scale ;
        if (workX[i] >= compsor->jpg->frame->width)
            workX[i] = compsor->jpg->frame->width - 1;
        workY[i] = (yLineNo * __MCU_HEIGHT + i) * 100 / compsor->scale ;
    }

    for (k = 0; k < compsor->readerCount; k++) {

        mcu = compsor->lineReader[k] ;

        startY = endY ;
        while(endY < 16 && nmsMcuCmpYPixel(mcu, workY[endY]) == 0)
            endY++ ;

        __NMS_ASSERT_DEFENSE(startY < endY, return -1) ;

        {
            for (x = compsor->nextX; x < __MCU_WIDTH; x++) {

                if (nmsMcuCmpXPixel(mcu, workX[x]) != 0) {
                    nmsMcuReadNext(mcu) ;
                    __NMS_ASSERT_DEFENSE(nmsMcuCmpXPixel(mcu, workX[x]) == 0, return -1) ;
                }

                for (y = startY ; y < endY; y++)
                    workRgbA[x + y * 16] = nmsJpegGetRgb(&nmsRgbCache, workX[x] - mcu->x, workY[y] - mcu->y) ;
            }

            /* Check if current MCU have next compress MCU data */
            if (nextX[0] == 0) {
                nextX[0] = ((xLineNo + 1) * __MCU_WIDTH) * 100 / compsor->scale ;
                if (nmsMcuCmpXPixel(mcu, nextX[0]) == 0) {
                    for (i = 1; i < 16; i++) {
                        nextX[i] = ((xLineNo + 1) * __MCU_WIDTH + i) * 100 / compsor->scale ;
                    }
                } else {
                    nextX[0] = -1 ;
                    nextXStart = 0 ;
                }
            }

            if (nextX[0] > 0) {
                for (x = 0; x < __MCU_WIDTH; x++) {
                    if (nmsMcuCmpXPixel(mcu, nextX[x]) != 0) {
                        nextXStart = x ;
                        break ;
                    }

                    for (y = startY ; y < endY; y++) {
                        nextRgbA[x + y * 16] = nmsJpegGetRgb(&nmsRgbCache, nextX[x] - mcu->x, workY[y] - mcu->y) ;
                    }
                }
            }
        }
    }

    compsor->nextX = nextXStart ;

    return 0 ;
}

/*
* NOT like the myJpeg project
* JpegC project always create the Yuv411 image .
*/

int nmsJpegCompress(struct __nms_jpeg* jpg, HFILE dumpHandle, UINT32 scale)
{
    int ret            = -1 ;
    int compWidth     = 0 ;
    int compHeight     = 0 ;
    int compMcuPerLine = 0 ;
    int compMcuLine    = 0 ;
    int i, j;

    nmsJpegCompressor* compsor = NULL ;

    if (scale < __NMS_MIN_SCALE) scale = __NMS_MIN_SCALE ;

    compWidth  = jpg->frame->width * scale / 100 ;
    compHeight = jpg->frame->height * scale / 100 ;

    compMcuPerLine = compWidth / __MCU_WIDTH + (((compWidth % __MCU_WIDTH) == 0) ?  0 : 1) ;
    compMcuLine    = compHeight / __MCU_HEIGHT + (((compHeight % __MCU_HEIGHT) == 0) ? 0 : 1) ;

    nprintf("nmsJpegCompress got scale = %d, compWidth = %d, compHeight = %d", scale, compWidth, compHeight) ;

    compsor = _createCompressor(jpg, dumpHandle, scale) ;

    nmsMcuInitCbCrIndexArray(jpg) ;

    _dumpHeader(jpg, dumpHandle, scale) ;

    for (i = 0; i < compMcuLine; i++) {

        if (_initLineReaders(compsor, i) < 0) {
            nprintf("nmsJpegCompress _initLineReaders error") ;
            goto _exit ;
        }

        for (j = 0; j < compMcuPerLine; j++) {

            if (_readCompMcu(compsor, j, i) < 0) {
                nprintf("nmsJpegCompress _readCompMcu error") ;
                goto _exit ;
            }

            _dumpMCU(compsor, compsor->rgbArray[compsor->workIndex]) ;
            compsor->workIndex = (compsor->workIndex == 0) ? 1 : 0 ;
        }
    }

    _dumpRemainBuf(compsor) ;

    /* Dump jpeg end maker */
    {
        UINT16 endMaker = 0xD9FF;
        int dummy       = 0 ;
        nmsFsWrite(dumpHandle, &endMaker, 2, &dummy) ;
    }

    ret = 0 ;

_exit:

    _destroyCompressor(compsor) ;

    return ret;
}

#ifdef __NMS_JPEG_DEBUG

static int _testDu(nmsJpegCompressor* compsor, int compId, int* duData)
{
    const UINT8* qt  = NULL;
    int tmpData[64]  = {0} ;
    int i            = 0 ;
    int zigIndex     = 0 ;
    int qtVal        = 0 ;

    /* IDT */
    nmsJpegDct(duData, tmpData) ;

    /* Quantization and Zig-Zag */
    qt = compsor->qt[compId] ;
    for (i = 0; i < 64; i++) {
        zigIndex  = _zigZagScanner[i] ;
        qtVal = qt[zigIndex] ;
        duData[i] = ((tmpData[zigIndex] + qtVal / 2) / qtVal) ;
    }

    return 0 ;
}

int nmsJpegTestReadDu(struct __nms_jpeg* jpg, int compId, int index, int* duData)
{
    static int rgbArray[256] = {0} ;

    static nmsJpegCompressor* _compsor = NULL ;

    int cbFactor[3] = {-1687, -3313, 5000} ;
    int crFactor[3] = {5000, -4187, -813} ;

    if (!_compsor) {
        int i , j ;
        int preDC[__NMC_JPEG_COMP_ID_CR + 1] = {0} ;
        nmsJpegMcu* mcu = nmsMcuCreate(jpg) ;
        nmsMcuInitCbCrIndexArray(jpg) ;
        nmsMcuInit(mcu, jpg->imgDataPos * 8, 0, preDC, NULL) ;
        nmsMcuReadNext(mcu) ;
        _compsor = _createCompressor(jpg, 0, 100) ;

        for (i = 0; i < 16; i++) {
            for (j = 0; j < 16; j++) {
                rgbArray[i * 16 + j] = nmsJpegGetRgb(&nmsRgbCache, j, i) ;
            }
        }
    }

    if (compId == __NMC_JPEG_COMP_ID_Y) {
        _getYDuData(rgbArray, index, duData) ;
        _testDu(_compsor, __NMC_JPEG_COMP_ID_Y, duData) ;
    } else if (compId == __NMC_JPEG_COMP_ID_CB) {
        _geCbCrDuData(rgbArray, duData, cbFactor) ;
        _testDu(_compsor, __NMC_JPEG_COMP_ID_CB, duData) ;
    } else if (compId == __NMC_JPEG_COMP_ID_CR) {
        _geCbCrDuData(rgbArray, duData, crFactor) ;
        _testDu(_compsor, __NMC_JPEG_COMP_ID_CR, duData) ;
    } else {
        nmsMemSet(duData, -1, 64 * 4) ;
    }

    return 0 ;
}
#endif

/* End of File */
